# app/routers/note.py
import json
import os
import uuid
from pathlib import Path
from typing import Optional, List
from urllib.parse import urlparse, unquote
import re
import zipfile
import io

from fastapi import APIRouter, HTTPException, BackgroundTasks, UploadFile, File
from pydantic import BaseModel, validator, field_validator
from dataclasses import asdict

from app.db.video_task_dao import get_task_by_video, get_all_tasks
from app.enmus.exception import NoteErrorEnum
from app.enmus.note_enums import DownloadQuality
from app.exceptions.note import NoteError
from app.services.note import NoteGenerator, logger
from app.utils.response import ResponseWrapper as R
from app.utils.url_parser import extract_video_id
from app.validators.video_url_validator import is_supported_video_url
from fastapi import APIRouter, Request, HTTPException
from fastapi.responses import StreamingResponse
import httpx
from app.enmus.task_status_enums import TaskStatus
from ..models.notes_model import NoteResult
from ..services.note_service import NoteService

# from app.services.downloader import download_raw_audio
# from app.services.whisperer import transcribe_audio

router = APIRouter(
    prefix="/note",
    tags=["note"]
)

note_service = NoteService()


class RecordRequest(BaseModel):
    video_id: str
    platform: str


class VideoRequest(BaseModel):
    video_url: str
    platform: str
    quality: DownloadQuality
    screenshot: Optional[bool] = False
    link: Optional[bool] = False
    model_name: str
    provider_id: str
    task_id: Optional[str] = None
    format: Optional[list] = []
    style: str = None
    extras: Optional[str]=None
    video_understanding: Optional[bool] = False
    video_interval: Optional[int] = 0
    grid_size: Optional[list] = []

    @field_validator("video_url")
    def validate_supported_url(cls, v):
        url = str(v)
        parsed = urlparse(url)
        if parsed.scheme in ("http", "https"):
            # 是网络链接，继续用原有平台校验
            if not is_supported_video_url(url):
                raise NoteError(code=NoteErrorEnum.PLATFORM_NOT_SUPPORTED.code,
                                message=NoteErrorEnum.PLATFORM_NOT_SUPPORTED.message)

        return v


NOTE_OUTPUT_DIR = os.getenv("NOTE_OUTPUT_DIR", "note_results")
UPLOAD_DIR = "uploads"


def save_note_to_file(task_id: str, note):
    os.makedirs(NOTE_OUTPUT_DIR, exist_ok=True)
    with open(os.path.join(NOTE_OUTPUT_DIR, f"{task_id}.json"), "w", encoding="utf-8") as f:
        json.dump(asdict(note), f, ensure_ascii=False, indent=2)


def run_note_task(task_id: str, video_url: str, platform: str, quality: DownloadQuality,
                  link: bool = False, screenshot: bool = False, model_name: str = None, provider_id: str = None,
                  _format: list = None, style: str = None, extras: str = None, video_understanding: bool = False,
                  video_interval=0, grid_size=[]
                  ):

    if not model_name or not provider_id:
        raise HTTPException(status_code=400, detail="请选择模型和提供者")

    note = NoteGenerator().generate(
        video_url=video_url,
        platform=platform,
        quality=quality,
        task_id=task_id,
        model_name=model_name,
        provider_id=provider_id,
        link=link,
        _format=_format,
        style=style,
        extras=extras,
        screenshot=screenshot
        , video_understanding=video_understanding,
        video_interval=video_interval,
        grid_size=grid_size
    )
    logger.info(f"Note generated: {task_id}")
    if not note or not note.markdown:
        logger.warning(f"任务 {task_id} 执行失败，跳过保存")
        return
    save_note_to_file(task_id, note)



@router.post('/delete_task')
def delete_task(data: RecordRequest):
    try:
        # TODO: 待持久化完成
        # NoteGenerator().delete_note(video_id=data.video_id, platform=data.platform)
        return R.success(msg='删除成功')
    except Exception as e:
        return R.error(msg=e)


@router.post("/upload")
async def upload(file: UploadFile = File(...)):
    os.makedirs(UPLOAD_DIR, exist_ok=True)
    file_location = os.path.join(UPLOAD_DIR, file.filename)

    with open(file_location, "wb+") as f:
        f.write(await file.read())

    # 假设你静态目录挂载了 /uploads
    return R.success({"url": f"/uploads/{file.filename}"})


@router.post("/generate_note")
def generate_note(data: VideoRequest, background_tasks: BackgroundTasks):
    try:

        video_id = extract_video_id(data.video_url, data.platform)
        # if not video_id:
        #     raise HTTPException(status_code=400, detail="无法提取视频 ID")
        # existing = get_task_by_video(video_id, data.platform)
        # if existing:
        #     return R.error(
        #         msg='笔记已生成，请勿重复发起',
        #
        #     )
        if data.task_id:
            # 如果传了task_id，说明是重试！
            task_id = data.task_id
            # 更新之前的状态
            NoteGenerator()._update_status(task_id, TaskStatus.PENDING)
            logger.info(f"重试模式，复用已有 task_id={task_id}")
        else:
            # 正常新建任务
            task_id = str(uuid.uuid4())

        background_tasks.add_task(run_note_task, task_id, data.video_url, data.platform, data.quality, data.link,
                                  data.screenshot, data.model_name, data.provider_id, data.format, data.style,
                                  data.extras, data.video_understanding, data.video_interval, data.grid_size)
        return R.success({"task_id": task_id})
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/task_status/{task_id}")
def get_task_status(task_id: str):
    status_path = os.path.join(NOTE_OUTPUT_DIR, f"{task_id}.status.json")
    result_path = os.path.join(NOTE_OUTPUT_DIR, f"{task_id}.json")

    # 优先读状态文件
    if os.path.exists(status_path):
        with open(status_path, "r", encoding="utf-8") as f:
            status_content = json.load(f)

        status = status_content.get("status")
        message = status_content.get("message", "")

        if status == TaskStatus.SUCCESS.value:
            # 成功状态的话，继续读取最终笔记内容
            if os.path.exists(result_path):
                with open(result_path, "r", encoding="utf-8") as rf:
                    result_content = json.load(rf)
                return R.success({
                    "status": status,
                    "result": result_content,
                    "message": message,
                    "task_id": task_id
                })
            else:
                # 理论上不会出现，保险处理
                return R.success({
                    "status": TaskStatus.PENDING.value,
                    "message": "任务完成，但结果文件未找到",
                    "task_id": task_id
                })

        if status == TaskStatus.FAILED.value:
            return R.error(message or "任务失败", code=500)

        # 处理中状态
        return R.success({
            "status": status,
            "message": message,
            "task_id": task_id
        })

    # 没有状态文件，但有结果
    if os.path.exists(result_path):
        with open(result_path, "r", encoding="utf-8") as f:
            result_content = json.load(f)
        return R.success({
            "status": TaskStatus.SUCCESS.value,
            "result": result_content,
            "task_id": task_id
        })

    # 什么都没有，默认PENDING
    return R.success({
        "status": TaskStatus.PENDING.value,
        "message": "任务排队中",
        "task_id": task_id
    })


@router.get("/image_proxy")
async def image_proxy(request: Request, url: str):
    headers = {
        "Referer": "https://www.bilibili.com/",
        "User-Agent": request.headers.get("User-Agent", ""),
    }

    try:
        # 先对 url 进行解码
        decoded_url = unquote(url)
        async with httpx.AsyncClient(timeout=10.0) as client:
            resp = await client.get(decoded_url, headers=headers)

            if resp.status_code != 200:
                raise HTTPException(status_code=resp.status_code, detail="图片获取失败")

            content_type = resp.headers.get("Content-Type", "image/jpeg")
            return StreamingResponse(
                resp.aiter_bytes(),
                media_type=content_type,
                headers={
                    "Cache-Control": "public, max-age=86400",  #  缓存一天
                    "Content-Type": content_type,
                }
            )
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/history")
def get_history():
    """获取所有笔记生成历史记录"""
    try:
        tasks = get_all_tasks()
        history_list = []
        for task in tasks:
            # 获取任务状态
            status_path = os.path.join(NOTE_OUTPUT_DIR, f"{task.task_id}.status.json")
            result_path = os.path.join(NOTE_OUTPUT_DIR, f"{task.task_id}.json")
            
            status = TaskStatus.PENDING.value
            title = ""
            cover_url = ""
            
            # 读取状态
            if os.path.exists(status_path):
                with open(status_path, "r", encoding="utf-8") as f:
                    status_content = json.load(f)
                    status = status_content.get("status", TaskStatus.PENDING.value)
            
            # 读取结果中的元数据
            if os.path.exists(result_path):
                with open(result_path, "r", encoding="utf-8") as f:
                    result_content = json.load(f)
                    audio_meta = result_content.get("audio_meta", {})
                    title = audio_meta.get("title", "")
                    cover_url = audio_meta.get("cover_url", "")
            
            history_list.append({
                "task_id": task.task_id,
                "video_id": task.video_id,
                "platform": task.platform,
                "status": status,
                "created_at": task.created_at.isoformat(),
                "title": title,
                "cover_url": cover_url
            })
        
        return R.success(data=history_list)
    except Exception as e:
        logger.error(f"获取历史记录失败: {e}")
        return R.error(msg="获取历史记录失败")


@router.get("/export/{task_id}")
async def export_note(task_id: str):
    """导出笔记为 ZIP 文件，包含 Markdown 和相关图片"""
    try:
        # 获取笔记内容
        note = note_service.get_note(task_id)
        if not note:
            raise HTTPException(status_code=404, detail="Note not found")
        
        # 创建内存中的 ZIP 文件
        zip_buffer = io.BytesIO()
        with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED, strict_timestamps=False) as zip_file:
            # 添加 Markdown 文件
            title = note.audio_meta.title if note.audio_meta else "note"
            # 移除文件名中的非法字符
            title = re.sub(r'[<>:"/\\|?*]', '_', title)
            md_filename = f"{title}.md"
            
            # 处理 Markdown 内容中的图片路径
            content = note.markdown
            image_paths = []
            
            # 获取平台和视频ID信息，用于定位截图
            platform = note.audio_meta.platform if note.audio_meta else None
            video_id = note.audio_meta.video_id if note.audio_meta else None
            
            logger.info(f"导出笔记: {title}, 平台: {platform}, 视频ID: {video_id}")
            
            # 查找所有图片引用
            for match in re.finditer(r'!\[.*?\]\((.*?)\)', content):
                img_path = match.group(1)
                logger.info(f"找到图片引用: {img_path}")
                
                # 处理相对路径和绝对路径
                if img_path.startswith('/static/'):
                    # 将绝对路径转换为相对路径
                    img_filename = os.path.basename(img_path)
                    new_path = f"images/{img_filename}"
                    content = content.replace(img_path, new_path)
                    
                    # 获取图片的实际路径
                    if platform and video_id and '/screenshots/' in img_path:
                        # 如果包含平台和视频ID信息，使用完整路径
                        if f"/{platform}/{video_id}/" in img_path:
                            # 图片路径包含平台和视频ID
                            actual_path = os.path.join(os.getcwd(), "static", "screenshots", 
                                                      platform, video_id, img_filename)
                        else:
                            # 尝试从路径中提取平台和视频ID
                            parts = img_path.split('/')
                            if len(parts) >= 5:  # /static/screenshots/platform/video_id/image.jpg
                                actual_path = os.path.join(os.getcwd(), *parts[1:])
                            else:
                                actual_path = os.path.join(os.getcwd(), "static", "screenshots", img_filename)
                    else:
                        # 简单路径
                        actual_path = os.path.join(os.getcwd(), "static", "screenshots", img_filename)
                    
                    logger.info(f"图片实际路径: {actual_path}")
                    if os.path.exists(actual_path):
                        image_paths.append(actual_path)
                    else:
                        logger.warning(f"图片文件不存在: {actual_path}")
                        
                        # 尝试在平台/视频ID子目录中查找
                        if platform and video_id:
                            alt_path = os.path.join(os.getcwd(), "static", "screenshots", 
                                                   platform, video_id, img_filename)
                            if os.path.exists(alt_path):
                                logger.info(f"在子目录中找到图片: {alt_path}")
                                image_paths.append(alt_path)
            
            # 如果没有找到任何图片，尝试在平台/视频ID目录下查找所有图片
            if not image_paths and platform and video_id:
                screenshots_dir = os.path.join(os.getcwd(), "static", "screenshots", platform, video_id)
                if os.path.exists(screenshots_dir) and os.path.isdir(screenshots_dir):
                    logger.info(f"尝试从目录中获取所有截图: {screenshots_dir}")
                    for img_file in os.listdir(screenshots_dir):
                        if img_file.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
                            img_path = os.path.join(screenshots_dir, img_file)
                            image_paths.append(img_path)
                            
                            # 在Markdown中添加图片引用（如果不存在）
                            img_ref = f"![](images/{img_file})"
                            if img_ref not in content:
                                content += f"\n\n{img_ref}"
            
            logger.info(f"找到 {len(image_paths)} 个图片文件")
            
            # 添加修改后的 Markdown 文件
            zip_file.writestr(zipfile.ZipInfo(md_filename), content.encode('utf-8'))
            
            # 添加图片文件
            for img_path in image_paths:
                if os.path.exists(img_path):
                    arcname = f"images/{os.path.basename(img_path)}"
                    zip_file.write(img_path, arcname)
                    logger.info(f"添加图片到ZIP: {arcname}")
        
        # 将 ZIP 缓冲区的指针移到开始
        zip_buffer.seek(0)
        
        # 返回 ZIP 文件
        filename = f"{title}.zip"
        encoded_filename = filename.encode('utf-8').decode('latin-1')
        headers = {
            "Content-Disposition": f"attachment; filename*=UTF-8''{encoded_filename}",
            "Content-Type": "application/zip",
        }
        return StreamingResponse(zip_buffer, headers=headers)
    except Exception as e:
        logger.error(f"导出笔记失败: {e}")
        raise HTTPException(status_code=500, detail=str(e))
